package com.stage.calculator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import com.stage.common.MathUtil;

public class HMM {

	private Integer[] stairSeq;
	private int win = 180;
	private int step = 30;
	private static final int UNKNOWN = 0;
	private static final int WAKE = 6;
	private static final int REM = 5;
	private static final int S1 = 4;
	private static final int S2 = 3;
	private static final int S3 = 2;
	private static final int S4 = 1;

	public static Double[][] AW;
	public static Double[][] BW;
	public static Double[][] AR;
	public static Double[][] BR;
	public static Double[][] A1;
	public static Double[][] B1;
	public static Double[][] A2;
	public static Double[][] B2;
	public static Double[][] A3;
	public static Double[][] B3;
	public static Double[][] A4;
	public static Double[][] B4;

	public static Double[] PI = { 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0,
			9.0 };

	public HMM(Integer[] seq) {
		this.stairSeq = seq;
	}

	public List<Integer> excute() {
		
//		System.out.println( " HMM.excute is starting ...... ");
		double pW;
		double pR;
		double p1;
		double p2;
		double p3;
		double p4;
		Integer[] range;
		Double[] p;
		double pMax;
		List<Integer> stage = new LinkedList<Integer>();
		
		
		for (int i = 0; i < stairSeq.length - win + 1; i += step) {
			
//			System.out.println("HMM applay data ......");
			range = Arrays.copyOfRange(stairSeq, i, i + win);
//			System.out.println("mean range is : " + MathUtil.mean(range));
			if (MathUtil.mean(range) < 20){
				stage.add(WAKE);
			}
			else {
				pW = forwardAlgorithm(range, PI, AW, BW);
				pR = forwardAlgorithm(range, PI, AR, BR);
				p1 = forwardAlgorithm(range, PI, A1, B1);
				p2 = forwardAlgorithm(range, PI, A2, B2);
				p3 = forwardAlgorithm(range, PI, A3, B3);
				p4 = forwardAlgorithm(range, PI, A4, B4);
				p = new Double[] { pW, pR, p1, p2, p3, p4 };
				pMax = MathUtil.max(p);

				/*
				 * switch中case的参数类型只能是int类型， 但是放byte，short，char类型的也可以是因为byte，
				 * short，shar可以自动提升（自动类型转换）为int，
				 * 所以归根到底还是int类型，这里说明一下java有8中数据类型：byte
				 * 、short、char、int、long、float、double，
				 * 还有一种boolean，其中boolean不能跟任何类型数据相互转化
				 * ，小的类型可以自动转化为大的数据类型，大的数据类型转化为小的必须要强制转换. jdk1.7开始支持String 因此用if
				 */
				if (pMax == 0){
					stage.add(S3);
				}
				else {
					if (pMax == pW) {
						stage.add(WAKE);
					} else if (pMax == p2) {
						stage.add(S2);
					} else if (pMax == p1) {
						stage.add(S1);
					} else if (pMax == p3) {
						stage.add(S3);
					} else if (pMax == pR) {
						stage.add(REM);
					} else if (pMax == p4) {
						stage.add(S4);
					} else {
						stage.add(UNKNOWN);
					}
				
				}
				
			
			}
			
		}
		for (int i=0;i<stage.size();i++) {
			switch (stage.get(i)) {
			case 0:
				stage.set(i, 0);
				break;
			case 1:
				stage.set(i, 1);
				break;
			case 2:
				stage.set(i, 1);
				break;
			case 3:
				stage.set(i, 2);
				break;
			case 4:
				stage.set(i, 2);
				break;
			case 5:
				stage.set(i, 3);
				break;
			case 6:
				stage.set(i, 4);
				break;
			}
		}
//		for (Integer h : stage){
//			System.out.println(" sleep stage is " + h);
//		}
		return stage;
	}

	/**
	 * 前向算法：已知模型参数，计算某一给定可观察状态序列的概率
	 * 
	 * @param O
	 *            观察序列
	 * @param PI
	 *            每种状态的初始概率
	 * @param A
	 *            状态转移矩阵：元素表示从一种状态转移到某一状态的概率
	 * @param B
	 *            混淆矩阵：元素表示在某种状态下观察到某个观察值的概率
	 * @return 某一给定可观察状态序列的概率
	 */
	public Double forwardAlgorithm(Integer[] O, Double[] PI, Double[][] A,
			Double[][] B) {
		if (O == null || PI == null || A == null || B == null) {
			return null;
		}
		// 初始输出概率
		Double PO = 0.0;
		// 观察序列的总时间
		int T = O.length;
		// 状态数
		int N = A.length;
		// 前向变量矩阵
		Double[][] alpha = new Double[T][N];
		// 计算t=1时刻的前向变量
		for (int i = 0; i < N; i++) {
			alpha[0][i] = PI[i] * B[i][(int) (O[0]-1)];
		}
		// 迭代计算t+1时刻的前向变量
		for (int t = 0; t < T - 1; t++) {
			for (int j = 0; j < N; j++) {
				Double sum = 0.0;
				for (int i = 0; i < N; i++) {
					sum += alpha[t][i] * A[i][j];
				}
				alpha[t + 1][j] = sum * B[j][(int) (O[t + 1]-1)];
			}
		}
		for (int i = 0; i < N; i++) {
			PO += alpha[T - 1][i];
		}
		return PO;
	}

	/**
	 * 读取文件中的A，B矩阵
	 * 
	 * @param file
	 *            文件名
	 * @return 矩阵
	 */
	public static Double[][] readArray(String file) {
		Double[][] array = null;
		List<List<Double>> list = new ArrayList<List<Double>>();
		try {
			BufferedReader in = new BufferedReader(new FileReader(new File(file)));
			String line=null;
			while ((line = in.readLine()) != null) {
				line = line.trim();
				List<Double> row = new ArrayList<Double>();
				String[] columns = line.split("\\s+");
				for (String e : columns) {
					row.add(Double.valueOf(e.trim()));
				}
				list.add(row);
			}
			in.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		array = new Double[list.size()][];
		for (int i = 0; i < list.size(); i++) {
			array[i] = list.get(i).toArray(new Double[list.get(i).size()]);
		}
		return array;
	}

	public int getWin() {
		return win;
	}

	public void setWin(int win) {
		this.win = win;
	}
}
